package net.event.data_processing.utils;

import org.eclipse.rdf4j.model.*;
import org.eclipse.rdf4j.model.impl.TreeModel;
import org.eclipse.rdf4j.model.vocabulary.RDF;
import org.eclipse.rdf4j.query.GraphQuery;
import org.eclipse.rdf4j.query.QueryResults;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.eclipse.rdf4j.repository.RepositoryResult;
import org.eclipse.rdf4j.repository.event.base.NotifyingRepositoryConnectionWrapper;
import org.eclipse.rdf4j.repository.event.base.RepositoryConnectionListenerAdapter;
import org.eclipse.rdf4j.repository.manager.RemoteRepositoryManager;
import org.eclipse.rdf4j.repository.manager.RepositoryManager;
import org.eclipse.rdf4j.rio.RDFFormat;

import java.io.File;
import java.util.List;

public class RDF4JService {
    RepositoryManager manager = null;

    public RDF4JService(String repositoryUrl) {
        manager = new RemoteRepositoryManager(repositoryUrl);
        manager.init();
    }

    public void loadTTL(String ttlFilePath, String repositoryId) {
        // Open a connection to the database
        try {
            RepositoryConnection conn = manager.getRepository(repositoryId).getConnection();
            File inputFile = new File(ttlFilePath);
            // add the RDF data from the inputstream directly to our database
            conn.add(inputFile, "", RDFFormat.TURTLE);
            //check
            RepositoryResult<Statement> result = conn.getStatements(null, null, null);
            while (result.hasNext()) {
                Statement st = result.next();
                System.out.println("db contains: " + st);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void loadTTLs(List<String> ttlFilePaths, String repositoryId) {
        // Open a connection to the database
        try {
            RepositoryConnection conn = manager.getRepository(repositoryId).getConnection();
            for (String ttlFilePath : ttlFilePaths) {
                System.out.println("read file:" + ttlFilePath);
                File inputFile = new File(ttlFilePath);
                // add the RDF data from the inputstream directly to our database
                conn.add(inputFile, "", RDFFormat.TURTLE);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void loadTTLsWithLog(List<String> ttlFilePaths, String repositoryId, int logInterval) {
        // Open a connection to the database
        Repository db = manager.getRepository(repositoryId);
        try {
            NotifyingRepositoryConnectionWrapper conn = new NotifyingRepositoryConnectionWrapper(db, db.getConnection());
            RepositoryConnectionListenerAdapter myListener =
                    new RepositoryConnectionListenerAdapter() {
                        private long count = 0;
                        private double lastTime = 0;

                        @Override
                        public void add(RepositoryConnection arg0, Resource arg1, IRI arg2,
                                        Value arg3, Resource... arg4) {
                            count++;
                            if (count % logInterval == 0) {
                                double cost = System.currentTimeMillis() / 1000.0 - lastTime;
                                System.out.println("Add statement number " + count + "\n"
                                        + arg1 + " " + arg2 + " " + arg3);
                                System.out.println("cost:" + cost + "s" + " speed: " + logInterval / cost + "/s");
                                lastTime = System.currentTimeMillis() / 1000.0;
                            }

                        }
                    };
            conn.addRepositoryConnectionListener(myListener);
            for (String ttlFilePath : ttlFilePaths) {
                System.out.println("read file:" + ttlFilePath);
                File inputFile = new File(ttlFilePath);
                conn.add(inputFile, "", RDFFormat.TURTLE);
            }
            //check
            RepositoryResult<Statement> result = conn.getStatements(null, null, null);
            while (result.hasNext()) {
                Statement st = result.next();
                System.out.println("db contains: " + st);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Model execConstructSparql(String repositoryId, String sparql) {
        Repository db = manager.getRepository(repositoryId);
        Model result = null;
        try {
            RepositoryConnection conn = db.getConnection();
            String sparqlWithPrefix = addPrefix(sparql, repositoryId);
            GraphQuery query = conn.prepareGraphQuery(sparqlWithPrefix);
            result = QueryResults.asModel(query.evaluate());
        } catch (Exception e) {
            e.printStackTrace();
        }
        db.shutDown();
        return result;
    }

    public String addPrefix(String query, String repositoryId) {
        String result = query;
        switch (repositoryId.toLowerCase()) {
            case "freebase":
                result = addPrefixIfNotIn(result, "fmbs", "http://framebase.org/ns/");
                result = addPrefixIfNotIn(result, "HOOM", "http://www.semanticweb.org/ontology/HOOM");
                result = addPrefixIfNotIn(result, "ORDO", "http://www.orpha.net/ORDO/");
                result = addPrefixIfNotIn(result, "Orphanet_", "http://www.orpha.net/ORDO/Orphanet_#");
                result = addPrefixIfNotIn(result, "bif", "bif:");
                result = addPrefixIfNotIn(result, "dawgt", "http://www.w3.org/2001/sw/DataAccess/tests/test-dawg#");
                result = addPrefixIfNotIn(result, "dbpedia", "http://dbpedia.org/resource/");
                result = addPrefixIfNotIn(result, "dbpprop", "http://dbpedia.org/property/");
                result = addPrefixIfNotIn(result, "dc", "http://purl.org/dc/elements/1.1/");
                result = addPrefixIfNotIn(result, "ebi", "http://www.ebi.ac.uk/efo/");
                result = addPrefixIfNotIn(result, "fn", "http://www.w3.org/2005/xpath-functions/#");
                result = addPrefixIfNotIn(result, "foaf", "http://xmlns.com/foaf/0.1/");
                result = addPrefixIfNotIn(result, "geo", "http://www.w3.org/2003/01/geo/wgs84_pos#");
                result = addPrefixIfNotIn(result, "go", "http://purl.org/obo/owl/GO#");
                result = addPrefixIfNotIn(result, "ldp", "http://www.w3.org/ns/ldp#");
                result = addPrefixIfNotIn(result, "math", "http://www.w3.org/2000/10/swap/math#");
                result = addPrefixIfNotIn(result, "mesh", "http://purl.org/commons/record/mesh/");
                result = addPrefixIfNotIn(result, "mf", "http://www.w3.org/2001/sw/DataAccess/tests/test-manifest#");
                result = addPrefixIfNotIn(result, "nci", "http://ncicb.nci.nih.gov/xml/owl/EVS/Thesaurus.owl#");
                result = addPrefixIfNotIn(result, "obo", "http://www.geneontology.org/formats/oboInOwl#");
                result = addPrefixIfNotIn(result, "ogc", "http://www.opengis.net/");
                result = addPrefixIfNotIn(result, "ogcgml", "http://www.opengis.net/ont/gml#");
                result = addPrefixIfNotIn(result, "ogcgs", "http://www.opengis.net/ont/geosparql#");
                result = addPrefixIfNotIn(result, "ogcgsf", "http://www.opengis.net/def/function/geosparql/");
                result = addPrefixIfNotIn(result, "ogcgsr", "http://www.opengis.net/def/rule/geosparql/");
                result = addPrefixIfNotIn(result, "ogcsf", "http://www.opengis.net/ont/sf#");
                result = addPrefixIfNotIn(result, "owl", "http://www.w3.org/2002/07/owl#");
                result = addPrefixIfNotIn(result, "product", "http://www.buy.com/rss/module/productV2/");
                result = addPrefixIfNotIn(result, "protseq", "http://purl.org/science/protein/bysequence/");
                result = addPrefixIfNotIn(result, "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
                result = addPrefixIfNotIn(result, "rdfa", "http://www.w3.org/ns/rdfa#");
                result = addPrefixIfNotIn(result, "rdfdf", "http://www.openlinksw.com/virtrdf-data-formats#");
                result = addPrefixIfNotIn(result, "rdfs", "http://www.w3.org/2000/01/rdf-schema#");
                result = addPrefixIfNotIn(result, "sc", "http://purl.org/science/owl/sciencecommons/");
                result = addPrefixIfNotIn(result, "scovo", "http://purl.org/NET/scovo#");
                result = addPrefixIfNotIn(result, "sd", "http://www.w3.org/ns/sparql-service-description#");
                result = addPrefixIfNotIn(result, "sioc", "http://rdfs.org/sioc/ns#");
                result = addPrefixIfNotIn(result, "skos", "http://www.w3.org/2004/02/skos/core#");
                result = addPrefixIfNotIn(result, "sql", "sql:");
                result = addPrefixIfNotIn(result, "vcard", "http://www.w3.org/2001/vcard-rdf/3.0#");
                result = addPrefixIfNotIn(result, "vcard2006", "http://www.w3.org/2006/vcard/ns#");
                result = addPrefixIfNotIn(result, "virtcxml", "http://www.openlinksw.com/schemas/virtcxml#");
                result = addPrefixIfNotIn(result, "virtrdf", "http://www.openlinksw.com/schemas/virtrdf#");
                result = addPrefixIfNotIn(result, "void", "http://rdfs.org/ns/void#");
                result = addPrefixIfNotIn(result, "xf", "http://www.w3.org/2004/07/xpath-functions");
                result = addPrefixIfNotIn(result, "xml", "http://www.w3.org/XML/1998/namespace");
                result = addPrefixIfNotIn(result, "xsd", "http://www.w3.org/2001/XMLSchema#");
                result = addPrefixIfNotIn(result, "xsl10", "http://www.w3.org/XSL/Transform/1.0");
                result = addPrefixIfNotIn(result, "xsl1999", "http://www.w3.org/1999/XSL/Transform");
                result = addPrefixIfNotIn(result, "xslwd", "http://www.w3.org/TR/WD-xsl");
                result = addPrefixIfNotIn(result, "yago", "http://dbpedia.org/class/yago/");
                break;
            case "yago":
                result = addPrefixIfNotIn(result, "fmbs", "http://framebase.org/ns/");
                result = addPrefixIfNotIn(result, "yago", "http://yago-knowledge.org/resource/");
                result = addPrefixIfNotIn(result, "dbp", "http://dbpedia.org/ontology/");
                result = addPrefixIfNotIn(result, "owl", "http://www.w3.org/2002/07/owl#");
                result = addPrefixIfNotIn(result, "rdf", "http://www.w3.org/1999/02/22-rdf-syntax-ns#");
                result = addPrefixIfNotIn(result, "rdfs", "http://www.w3.org/2000/01/rdf-schema#");
                result = addPrefixIfNotIn(result, "skos", "http://www.w3.org/2004/02/skos/core#");
                result = addPrefixIfNotIn(result, "xsd", "http://www.w3.org/2001/XMLSchema#");
                result = "base <http://yago-knowledge.org/resource/>\n" + result;
                break;
            case "wikidata":
                result = addPrefixIfNotIn(result, "", "http://framebase.org/ns/");
                result = addPrefixIfNotIn(result, "dbpedia-owl", "http://dbpedia.org/ontology/");
                break;
            case "dbpedia":
                result = addPrefixIfNotIn(result, "", "http://framebase.org/ns/");
                result = addPrefixIfNotIn(result, "dbpedia-owl", "http://dbpedia.org/ontology/");
                break;
            default:
                break;
        }
        return result;
    }

    public String addPrefixIfNotIn(String query, String prefix, String url) {
        if (prefix.length() > 0 && query.replaceAll("\\s", "").contains("prefix" + prefix + ":")) {
            return query;
        } else {
            return "prefix " + prefix + ": <" + url.replaceAll("[<>]", "") + ">\n" +
                    query;
        }
    }


    public static void main(String[] args) {

    }
}
